package com.broada.apm.util;

import groovy.lang.GroovyShell;
import groovy.lang.Script;

import java.io.File;
import java.io.FileWriter;
import java.io.FilenameFilter;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.broada.apm.entity.PacketCapture;
import com.broada.apm.entity.PacketDump;
import com.broada.apm.indicator.api.common.ApmDataIndicatorException;
import com.broada.apm.utils.ApmFileUtils;
import com.broada.apm.utils.ConfigureProperties;
import com.broada.apm.utils.ExceptionUtils;

/**
 * 抓包工具类
 * 
 * @author louyi Create By 2015年8月10日 上午9:29:15
 */
public class PacketCaptureManager {
	private static Logger log = LoggerFactory.getLogger(SystemManager.class);
	private static SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMddHHmmss");
	private static ExecutorService threadPool = Executors.newFixedThreadPool(5);
	private static File groovyFilePath = null;
	private static File downLoadPath = null;
	private static File dumpPath = null;
	private static final String SUFFIX = ".pcap";
	static {
		init();
	}

	private static void init() {
		String srciptsFilePath = System.getProperty("scripts.path", "../webapps/front/WEB-INF/scripts");
		groovyFilePath = ApmFileUtils.getFile(srciptsFilePath + "/packetCapture.groovy");
		downLoadPath = ApmFileUtils.getFile("../packetCapture/download/");
		dumpPath = ApmFileUtils.getFile(ConfigureProperties.getString("apm.analyze.apmdump",
				"../../analyze/dist/script/apmdump.sh"));
		if (!downLoadPath.exists())
			downLoadPath.mkdirs();
	}

	/**
	 * 抓包规则封装api 组织抓包规则，文件输出名
	 * 
	 * @param packetCapture 抓包规则实体类
	 * @return 包名，唯一编号
	 * @throws ApmDataIndicatorException
	 * @throws IOException
	 */
	public static String packetCapture(PacketCapture packetCapture) throws ApmDataIndicatorException, IOException {
		String packetCaptureRule = "\"" + packetCapture.getHost() + "\" " + packetCapture.getTimes();
		// 唯一标示
		String packageName = packetCapture.getHost() + "_" + formatter.format(new Date());
		String osName = System.getProperty("os.name");
		String _outFilePath = osName.toLowerCase().startsWith("win") ? downLoadPath.getAbsolutePath() + "\\" + packageName + SUFFIX
				: downLoadPath.getAbsolutePath() + "/" + packageName + SUFFIX;
		threadPool.execute(new PacketCaptureManager().new PacketCaptureTask(packetCaptureRule, packageName));
		try (FileWriter w = new FileWriter(_outFilePath + ".task")) {
			w.write(new Date().toString());
		};
		return packageName;
	}

	/**
	 * 执行api
	 * 
	 * @param packetCaptureRule
	 * @param packageName
	 */
	private void execute(String packetCaptureRule, String packageName) {
		String osName = System.getProperty("os.name");
		GroovyShell groovyShell = new GroovyShell();
		String _dumpPath = dumpPath.getAbsolutePath();
		String _outFilePath = osName.toLowerCase().startsWith("win") ? downLoadPath.getAbsolutePath() + "\\" + packageName + SUFFIX
				: downLoadPath.getAbsolutePath() + "/" + packageName + SUFFIX;
		try {
			Script scrpt = groovyShell.parse(groovyFilePath);
			@SuppressWarnings("unchecked")
			HashMap<String, Object> result = (LinkedHashMap<String, Object>) scrpt.invokeMethod("packetCapture", new String[] {
			_dumpPath, packetCaptureRule, _outFilePath
			});
			if (result != null) {
				String dumpInfo = (String) result.get("outInfo");
				String errorInfo = (String) result.get("errInfo");
				try (FileWriter r = new FileWriter(_outFilePath + ".out")) {
					r.write(dumpInfo + "\n");
					r.write(errorInfo);
					r.flush();
				};
			}
		} catch (Exception e) {
			ExceptionUtils.error(log, "抓包发生错误:", e);
			try (FileWriter r = new FileWriter(_outFilePath + ".out")) {
				r.write("抓包发生错误:\n");
				r.write("抓包发生内部错误,详见日志...");
				r.flush();
			} catch (IOException e1) {
				ExceptionUtils.error(log, "抓包过程发生错误", e1);
			};

		}
	}

	public static List<PacketDump> queryPacketDump() throws ApmDataIndicatorException {
		final FilenameFilter filter = (dir, name) -> {
			return name.endsWith(".task");
		};
		String osName = System.getProperty("os.name");
		List<PacketDump> pds = new ArrayList<PacketDump>();
		String[] tasks = downLoadPath.list(filter);
		if (tasks == null || tasks.length == 0)
			return pds;
		for (String fname : tasks) {
			String id = fname.substring(0, fname.length() - ".task".length());
			String status = PacketDump.UNFINISHED;
			PacketDump o = new PacketDump();
			File file = osName.toLowerCase().startsWith("win") ? new File(downLoadPath + "\\" + id) : new File(downLoadPath + "/" + id);
			if (file.exists()) {
				status = PacketDump.FINISH;
				try {
					File ret = new File(downLoadPath, id + ".out");
					if (ret.exists()) {
						o.setDumpInfo(FileUtils.readFileToString(ret));
					}
				} catch (IOException e) {
					ExceptionUtils.error(log, "获取抓包信息出错", e);
				}
				o.setId(id);
				o.setName(id);
				o.setStatus(status);
				o.setCreateTime(new Date(file.lastModified()));
				o.setUrl(fname);
				pds.add(o);
			}
		}
		Collections.sort(pds, (one, two) -> {
			return two.getCreateTime().compareTo(one.getCreateTime());
		});
		return pds;
	}

	public static void deleteFile(String fileName) {
		String osName = System.getProperty("os.name");
		if (StringUtils.isNotBlank(fileName)) {
			File file = osName.toLowerCase().startsWith("win") ? new File(downLoadPath + "\\" + fileName) : new File(downLoadPath + "/" + fileName);
			if (file.exists()) {
				file.delete();
			}
			file = osName.toLowerCase().startsWith("win") ? new File(downLoadPath + "\\" + fileName + ".task") : new File(downLoadPath + "/" + fileName + ".task");
			if (file.exists()) {
				file.delete();
			}
			file = osName.toLowerCase().startsWith("win") ? new File(downLoadPath + "\\" + fileName + ".out") : new File(downLoadPath + "/" + fileName + ".out");
			if (file.exists()) {
				file.delete();
			}
		}
	}

	public static File downloadPacket(String packetDumpId) {
		// 不同系统下downLoadPath + "/" + packetDumpId会有所不同，Windows下用"\\", Lunix下用"/"
		String osName = System.getProperty("os.name");
		return osName.toLowerCase().startsWith("win") ? new File(downLoadPath + "\\" + packetDumpId) : new File(downLoadPath + "/" + packetDumpId);
	}

	class PacketCaptureTask implements Runnable {
		private String packetCaptureRule;
		private String packageName;

		public PacketCaptureTask() {
		}

		// 周期、抓包规则、输出文件名
		public PacketCaptureTask(String packetCaptureRule, String packageName) {
			this.packageName = packageName;
			this.packetCaptureRule = packetCaptureRule;
		}

		public void run() {
			execute(packetCaptureRule, packageName);
		}
	}
}
